package com.appbee.songs.server.dao;

import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.appbee.core.server.dao.BaseDAO;
import com.appbee.songs.server.model.Song;
import com.appbee.songs.server.model.SongFilter;
import com.appbee.songs.shared.FilterField;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.code.twig.FindCommand.RootFindCommand;
import com.google.code.twig.Restriction;

public class SongDAO extends BaseDAO<Song>
{
	private static final Logger log = Logger.getLogger(SongDAO.class.getName());

	public List<Song> findByFilter(final SongFilter filter, int start, int size)
	{
		try
		{
			log.log(Level.INFO, "findByFilter: filter:" + filter.getValue()
					+ ", start: "
					+ start + ", size: " + size);
			
			RootFindCommand<Song> find = objectDatastore.find()
					.type(Song.class);
			addSort(filter, find);
			if (filter.hasValue())
			{
				final String filterLower = filter.getValue().toLowerCase();
				find.restrictEntities(new Restriction<Entity>()
				{
					
					@Override
					public boolean allow(Entity candidate)
					{
						FilterField f = filter.getField();
						if(f==FilterField.All)
							return contains(candidate, "name") || contains(candidate,"artist") || contains(candidate,"album");
						if(f==FilterField.Name) return contains(candidate,"name");
						if(f==FilterField.Artist) return contains(candidate,"artist");
						if(f==FilterField.Album) return contains(candidate,"album");
						if(f==FilterField.Genre) return contains(candidate,"gere");
						if(f==FilterField.StartingPlayer) return contains(candidate,"startingPlayer");
						if(f==FilterField.KeyTone) return contains(candidate,"keyTone");
						
						return false;
					}
					
					public boolean contains(Entity candidate,String name)
					{
						Object o = candidate.getProperty(name);
						if(o!=null && ((String)o).toLowerCase().contains(filterLower))
							return true;
						return false;
					}
				});
				List<Song> list = find.returnAll().now();
				log.info("List size: " + list.size());
				return list.subList(Math.min(list.size(), start),
						Math.min(list.size(), start + size));
			}
			else
			{
				find.startFrom(start);
				find.fetchMaximum(size);
				List<Song> list = find.returnAll().now();
				log.info("List size: " + list.size());
				return list;
			}
		}
		catch (Throwable e)
		{
			log.info("Villa: " + e.getMessage());
			log.log(Level.SEVERE, "Villa í find by filter ", e);
			throw new RuntimeException(e);
		}
	}

	private void addSort(final SongFilter filter, RootFindCommand<Song> find)
	{
		if (filter.getSortField() != null && filter.getSortField().length() > 0)
		{
			log.info("Sort field: " + filter.getSortField());
			find.addSort(filter.getSortField(),
					filter.getAscending() ? SortDirection.ASCENDING
							: SortDirection.DESCENDING);
		}
		else
		{
			find.addSort("number");
		}
	}

	@Override
	public void store(Song entity)
	{
		super.store(entity);
	}

	@Override
	public void update(Song entity)
	{
		super.update(entity);
	}

	public Integer getMaxNumber()
	{
		Query q = new Query("com_appbee_songs_server_model_Song");
		q.addSort("number", SortDirection.DESCENDING);
		PreparedQuery pq = getObjectDatastore().getService().prepare(q);
		Iterator<Entity> iterator = pq.asIterable().iterator();
		if (iterator.hasNext())
		{
			return ((Number) iterator.next().getProperty("number")).intValue();
		}
		return new Integer(0);
	}

	public Song updateNumber(Song song, int number)
	{
		RootFindCommand<Song> find = objectDatastore.find().type(Song.class);
		if (number > song.getNumber())
		{
			find.addFilter(	"number",
							FilterOperator.GREATER_THAN,
							song.getNumber());
			find.addFilter("number", FilterOperator.LESS_THAN_OR_EQUAL, number);
		}
		else
		{
			find.addFilter("number", FilterOperator.LESS_THAN, song.getNumber());
			find.addFilter(	"number",
							FilterOperator.GREATER_THAN_OR_EQUAL,
							number);
		}
		List<Song> list = find.returnAll().now();
		for (Song s : list)
		{
			int n = s.getNumber();
			n = number > song.getNumber() ? n - 1 : n + 1;
			s.setNumber(n);
			objectDatastore.update(s);
		}
		song.setNumber(number);
		update(song);
		objectDatastore.activate(song);
		return song;
	}

}
